package vn.fpt.fsoft.maingame;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import vn.fpt.fsoft.coreframework.Board;
import vn.fpt.fsoft.coreframework.Human;
import vn.fpt.fsoft.coreframework.Player;
import vn.fpt.fsoft.coreframework.ProjectileManager;

/**
 * main class for the game as a Swing application
 * @author DoN
 */
public class GameMain extends JFrame {

    // Define constants for the game
    static final int CANVAS_WIDTH = 800; // width and height of the game screen
    static final int CANVAS_HEIGHT = 600; // width and height of the game screen
    static final int UPDATE_RATE = 60; // number of game update per second
    static final long UPDATE_PERIOD = 1000000000L / UPDATE_RATE; // nanoseconds

    // Enumeration for the states of the game.
    static enum State {
        INITIALIZED, PLAYING, PAUSED, GAMEOVER, DESTROYED
    }

    static State state; // current state of the game

    // Define instance variables for the game objects here
    
    // Handle for the custom drawing panel
    private GameCanvas canvas;
    // map
    private Board map;
    
    private Human player;
    
    private ProjectileManager projectileManager;

    // Constructor to initialize the UI components and game objects
    public GameMain() {
        // Initialize the game objects
        gameInit();

        // UI components
        canvas = new GameCanvas();
        canvas.setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
        this.setContentPane(canvas);

        // Other UI components such as button, score board, if any.

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.pack();
        this.setTitle("MY GAME");
        this.setVisible(true);

        // Start the game.
        gameStart();
    }

    // All the game related codes here

    // Initialize all the game objects, run only once in the constructor of the
    // main class.
    public void gameInit() {
        // init map
        map = new Board();
        map.loadMap("maps/round1.txt");

        player = new Human("ABC");
        player.initPlayer();
        state = State.INITIALIZED;
        
        projectileManager = new ProjectileManager();
    }

    // Shutdown the game, clean up code that runs only once.
    public void gameShutdown() {
        // clear game here......
    }

    // To start and re-start the game.
    public void gameStart() {
        // Create a new thread
        Thread gameThread = new Thread() {
            // Override run() to provide the running behavior of this thread.
            @Override
            public void run() {
                gameLoop();
            }
        };
        // Start the thread. start() calls run(), which in turn calls
        // gameLoop().
        gameThread.start();
    }

    // Run the game loop here.
    private void gameLoop() {
        // Regenerate and reset the game objects for a new game
        if (state == State.INITIALIZED || state == State.GAMEOVER) {
            // Generate a new object here
            state = State.PLAYING;
        }

        // Game loop
        long beginTime, timeTaken, timeLeft;
        while (true) {
            beginTime = System.nanoTime();
            if (state == State.GAMEOVER)
                break; // break the loop to finish the current play
            if (state == State.PLAYING) {
                // Update the state and position of all the game objects,
                // detect collisions and provide responses.
                gameUpdate();
            }
            // Refresh the display
            repaint();
            // Delay timer to provide the necessary delay to meet the target
            // rate
            timeTaken = System.nanoTime() - beginTime;
            timeLeft = (UPDATE_PERIOD - timeTaken) / 1000000; // in milliseconds
            if (timeLeft < 10)
                timeLeft = 10; // set a minimum
            try {
                // Provides the necessary delay and also yields control so that
                // other thread can do work.
                Thread.sleep(timeLeft);
            } catch (InterruptedException ex) {
            }
        }
    }

    // Update the state and position of all the game objects,
    // detect collisions and provide responses.
    public void gameUpdate() {
        // update logic here
    	
    	projectileManager.update();
    }

    // Refresh the display. Called back via repaint(), which invoke the
    // paintComponent().
    private void gameDraw(Graphics2D g2d) {
        // draw map
        map.drawBackGround(g2d);

        player.draw(g2d);
        
        projectileManager.draw(g2d);
        switch (state) {
            case INITIALIZED :
                // ......
                break;
            case PLAYING :
                // ......
                break;
            case PAUSED :
                // ......
                break;
            case GAMEOVER :
                // ......
                break;
        }
        // ......
    }

    // Process a key-pressed event. Update the current state.
    public void gameKeyPressed(int keyCode) {
    	
    	player.handleInput(keyCode);
        switch (keyCode) {
            case KeyEvent.VK_UP :
                // ......
                break;
            case KeyEvent.VK_DOWN :
                // ......
                break;
            case KeyEvent.VK_LEFT :
                // ......
                break;
            case KeyEvent.VK_RIGHT :
                // ......
                break;
            case KeyEvent.VK_SPACE:
            	projectileManager.addProjectile(player);
            	break;
        }
    }

    // Process a key-released event.
    public void gameKeyReleased(int keyCode) {
    }

    // Process a key-typed event.
    public void gameKeyTyped(char keyChar) {
    }

    // Other methods
    // ......

    // Custom drawing panel, written as an inner class.
    class GameCanvas extends JPanel implements KeyListener {
        // Constructor
        public GameCanvas() {
            setFocusable(true); // so that can receive key-events
            requestFocus();
            addKeyListener(this);
        }

        // Override paintComponent to do custom drawing.
        // Called back by repaint().
        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            super.paintComponent(g2d); // paint background
            setBackground(Color.BLACK); // may use an image for background

            // Draw the game objects
            gameDraw(g2d);
        }

        // KeyEvent handlers
        @Override
        public void keyPressed(KeyEvent e) {
            gameKeyPressed(e.getKeyCode());
        }

        @Override
        public void keyReleased(KeyEvent e) {
            gameKeyReleased(e.getKeyCode());
        }

        @Override
        public void keyTyped(KeyEvent e) {
            gameKeyTyped(e.getKeyChar());
        }
    }

    // main
    public static void main(String[] args) {
        // Use the event dispatch thread to build the UI for thread-safety.
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GameMain();
            }
        });
    }
}
